package com.example.kotlinExample.aop


import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.AfterThrowing
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.MethodSignature
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestAttributes
import org.springframework.web.context.request.RequestContextHolder
import java.lang.reflect.Method
import java.util.*
import javax.servlet.http.HttpServletRequest


/**
 *
 *
 * @author liliang
 */
@Aspect
@Component
class OperLogAspect {

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.example.kotlinExample.annotation.OperLog)")
    fun operLogPoinCut() {
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
    @Pointcut("execution(* com.example.kotlinExample.controller.*.*(..))")
    fun operExceptionLogPoinCut() {
    }

    /**
     * 正常返回通知，拦截用户操作日志，连接点正常执行完成后执行， 如果连接点抛出异常，则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    fun saveOperLog(joinPoint: JoinPoint, keys: Any?) {
        // 获取RequestAttributes
        val requestAttributes = RequestContextHolder.getRequestAttributes()
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        val request = requestAttributes?.resolveReference(RequestAttributes.REFERENCE_REQUEST) as HttpServletRequest?

        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            val signature = joinPoint.signature as MethodSignature
            // 获取切入点所在的方法
            val method: Method = signature.method
            // 获取操作
            // 获取请求的类名
            val className = joinPoint.target.javaClass.name
            // 获取请求的方法名
            var methodName: String = method.name
            methodName = "$className.$methodName"

            // 请求的参数
            val rtnMap = converMap(request!!.parameterMap)
            // 将参数所在的数组转换成json

        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 异常返回通知，用于拦截异常日志信息 连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    fun saveExceptionLog(joinPoint: JoinPoint, e: Throwable) {
        // 获取RequestAttributes
        val requestAttributes = RequestContextHolder.getRequestAttributes()
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        val request = requestAttributes?.resolveReference(RequestAttributes.REFERENCE_REQUEST) as HttpServletRequest?

        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            val signature = joinPoint.signature as MethodSignature
            // 获取切入点所在的方法
            val method: Method = signature.method

            // 获取请求的类名
            val className = joinPoint.target.javaClass.name
            // 获取请求的方法名
            var methodName: String = method.name
            methodName = "$className.$methodName"
            // 请求的参数
            val rtnMap = converMap(request!!.parameterMap)
            // 将参数所在的数组转换成json
        } catch (e2: Exception) {
            e2.printStackTrace()
        }
    }

    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    fun converMap(paramMap: Map<String, Array<String?>?>): Map<String, String> {
        val rtnMap: MutableMap<String, String> = HashMap()
        for (key in paramMap.keys) {
            rtnMap[key] = (paramMap[key] ?: error(""))[0]!!
        }
        return rtnMap
    }

    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    fun stackTraceToString(exceptionName: String, exceptionMessage: String?, elements: Array<StackTraceElement>): String? {
        val strbuff = StringBuffer()
        for (stet in elements) {
            strbuff.append("""$stet""".trimIndent())
        }
        return "$exceptionName:$exceptionMessage\n\t$strbuff"
    }
}